/*
 * Copyright (c) 2015-2016, Texas Instruments Incorporated
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * *  Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *
 * *  Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * *  Neither the name of Texas Instruments Incorporated nor the names of
 *    its contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

/***** Includes *****/
#include <stdlib.h>
#include <xdc/std.h>
#include <xdc/cfg/global.h>
#include <xdc/runtime/System.h>

#include <ti/sysbios/BIOS.h>
#include <ti/sysbios/knl/Task.h>
#include <ti/sysbios/knl/Clock.h>
#include <ti/sysbios/knl/Event.h>

/* Drivers */
#include <ti/drivers/rf/RF.h>
#include <ti/drivers/PIN.h>

#ifdef DEVICE_FAMILY
    #undef DEVICE_FAMILY_PATH
    #define DEVICE_FAMILY_PATH(x) <ti/devices/DEVICE_FAMILY/x>
    #include DEVICE_FAMILY_PATH(driverlib/rf_prop_mailbox.h)
#else
    #error "You must define DEVICE_FAMILY at the project level as one of cc26x0, cc26x0r2, cc13x0, etc."
#endif

/* Board Header files */
#include "Board.h"

#include "rfBleNcTx.h"
#include "RFQueue.h"
#include "smartrf_settings/smartrf_settings_ble.h"

#include <stdlib.h>

#define ms_To_RadioTime(ms) (ms*(4000000/1000))

/***** Defines *****/
#define BLE_NC_TASK_STACK_SIZE 4096
#define BLE_NC_TASK_PRIORITY   2

#define BLE_BEACON_PERIOD_MS    200

#define BLE_RF_EVENT_MASK  ( RF_EventLastCmdDone | RF_EventCmdError | \
             RF_EventCmdAborted | RF_EventCmdStopped | RF_EventCmdCancelled )

#define BLENC_TASK_EVENT_ALL    0xFFFFFFFF
#define BLENC_TASK_EVENT_SCH    (uint32_t)(1 << 0)

#define MAX_CMDS    10

typedef struct {
    rfc_CMD_BLE_ADV_NC_t *pBleNcCmd;
    rfc_bleAdvOutput_t advStats;
    RF_ScheduleCmdParams bleSchParams;
    RF_CmdHandle ch;
} bleNcCmdQ_t;

bleNcCmdQ_t bleNcCmdQ[MAX_CMDS] = {0};

/***** Prototypes *****/
static void bleNcTaskFunction(UArg arg0, UArg arg1);

/***** Variable declarations *****/
static Task_Params bleNcTaskParams;
Task_Struct bleNcTask;    /* not static so you can see in ROV */
static uint8_t bleNcTaskStack[BLE_NC_TASK_STACK_SIZE];

static RF_Object rfBleObject;
static RF_Handle rfBleHandle;

Clock_Struct scheduleTimerStruct;

static PIN_Handle pinHandle;

Event_Struct bleNcEvent;  /* not static so you can see in ROV */
static Event_Handle BleNcEventHandle;

static uint8_t localNameAdvertisement[] = {
        0x02, //Length of this Data section
        0x01, //<<Flags>>
        0x02, //LE General Discoverable Mode
        0x18, //Length of this Data section
        0x09, //<<Complete local name>>
        'C', 'C', '1', '3', '5', '0', ' ',
        'L', 'a', 'u', 'n', 'c', 'h',
        'p', 'a', 'd', ' ', '-', ' ', '0', 'x', '0', '0'
        };


static int32_t scheduleBLeAdvertisement(uint32_t time);

/*
 *  ======== scheduleTimerFxn =======
 */
Void scheduleTimerFxn(UArg arg0)
{
    Event_post(BleNcEventHandle, BLENC_TASK_EVENT_SCH);
}

//Callback for Async Tx complete
static void txDoneCallback(RF_Handle h, RF_CmdHandle ch, RF_EventMask e)
{
    uint32_t cmdIdx;
    //find command
    for(cmdIdx = 0; cmdIdx < MAX_CMDS; cmdIdx++)
    {
        if ((bleNcCmdQ[cmdIdx].ch == ch) && (bleNcCmdQ[cmdIdx].pBleNcCmd != 0))
        {
            break;
        }
    }

    if(cmdIdx == MAX_CMDS)
    {
        //unknown command
        return;
    }

    if ((e & RF_EventLastCmdDone) && (bleNcCmdQ[cmdIdx].advStats.nTxAdvInd == 1))
    {
        //PIN_setOutputValue(pinHandle, BLE_SUCCESS, 1);
    }
    else if (e & ( RF_EventCmdAborted | RF_EventCmdStopped | RF_EventCmdCancelled))
    {
        PIN_setOutputValue(pinHandle, BLE_ABORT, 1);
    }
    else
    {
        //PIN_setOutputValue(pinHandle, BLE_FAIL, 1);
    }

    PIN_setOutputValue(pinHandle, BLE_ABORT, 0);

    free(bleNcCmdQ[cmdIdx].pBleNcCmd);
    bleNcCmdQ[cmdIdx].pBleNcCmd = 0;

}

/***** Function definitions *****/
void bleNcTask_init(PIN_Handle ledPinHandle) {

    pinHandle = ledPinHandle;
    Clock_Params clkParams;
    /* Create event used internally for state changes */
    Event_Params eventParam;
    Event_Params_init(&eventParam);
    Event_construct(&bleNcEvent, &eventParam);
    BleNcEventHandle = Event_handle(&bleNcEvent);

    Clock_Params_init(&clkParams);
    clkParams.period = BLE_BEACON_PERIOD_MS * 1000 / Clock_tickPeriod;
    clkParams.startFlag = TRUE;

    /* Construct a periodic Clock Instance */
    Clock_construct(&scheduleTimerStruct, (Clock_FuncPtr)scheduleTimerFxn,
                    BLE_BEACON_PERIOD_MS * 1000 / Clock_tickPeriod,
                    &clkParams);

    Task_Params_init(&bleNcTaskParams);
    bleNcTaskParams.stackSize = BLE_NC_TASK_STACK_SIZE;
    bleNcTaskParams.priority = BLE_NC_TASK_PRIORITY;
    bleNcTaskParams.stack = &bleNcTaskStack;
    bleNcTaskParams.arg0 = (UInt)1000000;

    Task_construct(&bleNcTask, bleNcTaskFunction, &bleNcTaskParams, NULL);
}

static void bleNcTaskFunction(UArg arg0, UArg arg1)
{
    RF_Params rfParams;
    RF_Params_init(&rfParams);

    RF_pModeBle->rfMode = RF_MODE_MULTIPLE;

    rfBleHandle = RF_open(&rfBleObject, RF_pModeBle,
                           (RF_RadioSetup*)RF_ble_pCmdRadioSetup, NULL);

    /* Enter main task loop */
    while (1)
    {
        uint32_t schStatus;

        /* Wait for event */
        uint32_t events = Event_pend(BleNcEventHandle, 0, BLENC_TASK_EVENT_ALL, BIOS_WAIT_FOREVER);

        /* If we got a new ADC sensor value */
        if (events & BLENC_TASK_EVENT_SCH)
        {
            static uint32_t prevTime = 0;
            uint32_t currTime = RF_getCurrentTime();
            prevTime = currTime;

            //Schedule 1s in advance
            schStatus = scheduleBLeAdvertisement(1000);
            if(schStatus != 0)
            {
                PIN_setOutputValue(pinHandle, BLE_ABORT, 0);
            }

        }
    }
}

static int32_t scheduleBLeAdvertisement(uint32_t time)
{
    RF_EventMask result;
    static uint32_t bleNcCmdIdx = 0;
    uint8_t deviceAddress[] = {0,1,2,3,4,5};
    uint8_t channel = 37;

    if (bleNcCmdQ[bleNcCmdIdx].pBleNcCmd != 0)
    {
        //Queue overflow
        return -1;
    }

    memset(&bleNcCmdQ[bleNcCmdIdx], 0, sizeof(bleNcCmdQ_t));
    bleNcCmdQ[bleNcCmdIdx].pBleNcCmd = malloc(sizeof(rfc_CMD_BLE_ADV_NC_t));

    if (bleNcCmdQ[bleNcCmdIdx].pBleNcCmd == 0)
    {
        //malloc failure
        return -2;
    }

    memcpy(bleNcCmdQ[bleNcCmdIdx].pBleNcCmd, RF_ble_pCmdBleAdvNc, sizeof(rfc_CMD_BLE_ADV_NC_t));

    bleNcCmdQ[bleNcCmdIdx].pBleNcCmd->startTrigger.triggerType = TRIG_ABSTIME;
    bleNcCmdQ[bleNcCmdIdx].pBleNcCmd->startTrigger.pastTrig = 0;
    bleNcCmdQ[bleNcCmdIdx].pBleNcCmd->startTime = RF_getCurrentTime() + ms_To_RadioTime(time);

    bleNcCmdQ[bleNcCmdIdx].bleSchParams.priority = RF_PriorityHigh;
    //BLE NC Adv should only take 1ms t execute
    bleNcCmdQ[bleNcCmdIdx].bleSchParams.endTime = bleNcCmdQ[bleNcCmdIdx].pBleNcCmd->startTime + ms_To_RadioTime(1);

    bleNcCmdQ[bleNcCmdIdx].pBleNcCmd->pParams->advLen = sizeof(localNameAdvertisement);

    bleNcCmdQ[bleNcCmdIdx].pBleNcCmd->pParams->pAdvData = (uint8_t*) localNameAdvertisement;
    bleNcCmdQ[bleNcCmdIdx].pBleNcCmd->pParams->pDeviceAddress = (uint16_t*) deviceAddress;

    //only useful for debug
    bleNcCmdQ[bleNcCmdIdx].pBleNcCmd->pOutput = &bleNcCmdQ[bleNcCmdIdx].advStats;

    bleNcCmdQ[bleNcCmdIdx].pBleNcCmd->channel = channel;
    bleNcCmdQ[bleNcCmdIdx].pBleNcCmd->whitening.init = 0x40 + channel;

    PIN_setOutputValue(pinHandle, BLE_TX, 1);
    bleNcCmdQ[bleNcCmdIdx].ch = RF_scheduleCmd(rfBleHandle, (RF_Op*)bleNcCmdQ[bleNcCmdIdx].pBleNcCmd, &bleNcCmdQ[bleNcCmdIdx].bleSchParams, txDoneCallback, BLE_RF_EVENT_MASK);
    //result = RF_pendCmd(rfBleHandle, cmdHdl, BLE_RF_EVENT_MASK);
    PIN_setOutputValue(pinHandle, BLE_TX, 0);

    if(bleNcCmdQ[bleNcCmdIdx].ch != -2)
    {
        bleNcCmdIdx++;
        if(bleNcCmdIdx == MAX_CMDS)
        {
            bleNcCmdIdx = 0;
        }
    }
    else
    {
        free(bleNcCmdQ[bleNcCmdIdx].pBleNcCmd);
        bleNcCmdQ[bleNcCmdIdx].pBleNcCmd = 0;

        return -3;
    }
    //Task_sleep(BLE_BEACON_PERIOD_MS * 1000 / Clock_tickPeriod);

    return 0;
}
